<?php

/*
 * Copyright (C) 2009 - 2011 Pham Cong Dinh
 *
 * This file is part of Spica.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

// namespace spica\core;

/**
 * This library provides facilities for parsing the command-line options in a standalone program.
 *
 * namespace spica\core\Console
 *
 * @todo       FIXME
 * 
 * @category   spica
 * @package    core
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 09, 2010
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Console.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaConsole
{
    /**
     * Parameter list.
     *
     * @var array
     */
    private $_shortOptList;

    /**
     * Parameter list.
     *
     * @var array
     */
    private $_longOptList;

    /**
     * Is command line options parsed?
     * 
     * @var bool
     */
    private $_parsed = false;

    /**
     * Parsed command line options.
     * 
     * @var array
     */
    private $_opt;

    /**
     * Stores the raw command-line arguments for the calling applicaion.
     *
     * @var array
     */
    private $_argv = array();

    /**
     * Stores the name of the calling applicaion.
     *
     * @var string
     */
    private $_programName;

    /**
     * Constructs an object of <code>SpicaConsole</code>.
     *
     * @param array $shortOpts An element of argv that starts with '-'
     * @param array $longOptList An element of argv that starts with '--' ( pair of dashes instead of the single dash)
     */
    public function __construct($shortOpts, $longOptList)
    {
        if (!isset($_SERVER['argv']))
        {
            if (false == ini_get('register_argc_argv'))
            {
                throw new Exception("argv is not available, because ini option 'register_argc_argv' is set Off");
            }

            throw new Exception('$_SERVER["argv"] is not set.');
        }

        $this->_shortOptList = $shortOpts;
        $this->_longOptList = $longOptList;
    }

    /**
     *
     * @param <type> $name
     * @param <type> $shortOpt
     * @return <type>
     */
    public function getOpt($name, $shortOpt = true)
    {
        if (true === $shortOpt && !isset($this->_shortOptList[$name]))
        {
            throw new InvalidArgumentException('Unregconized parameter: '.$name);
        }

        if (false === $this->_parsed)
        {
            $this->_programName = $_SERVER['argv'][0];

            if (!is_array($argv))
            {
                $argv = array_slice($_SERVER['argv'], 1);
            }

            $this->_argv = array_merge($this->_argv, $argv);
            $this->_opt = $this->_parseOptions($this->_shortOptList, $this->_longOptList);
        }

        if (isset($this->_opt[$name]))
        {
            return $this->_opt[$name];
        }

        return false;
    }

    /**
     * Parses $GLOBALS['argv'] for parameters and assigns them to an array.
     *
     * Supports:
     * -e
     * -e <value>
     * --long-param
     * --long-param=<value>
     * --long-param <value>
     * <value>
     *
     * @param array $noopt List of parameters without values
     */
    private function _parseOptions($noopt = array())
    {
        $result = array();
        $params = $GLOBALS['argv'];
        reset($params);
        
        while (list($tmp, $p) = each($params))
        {
            if ($p{0} == '-')
            {
                $pname = substr($p, 1);
                $value = true;
                if ($pname[0] == '-')
                {
                    // long-opt (--<param>)
                    $pname = substr($pname, 1);
                    if (strpos($p, '=') !== false)
                    {
                        // value specified inline (--<param>=<value>)
                        list($pname, $value) = explode('=', substr($p, 2), 2);
                    }
                }
                // check if next parameter is a descriptor or a value
                $nextparm = current($params);

                if (!in_array($pname, $noopt) && $value === true && $nextparm !== false && $nextparm[0] != '-')
                {
                    list($tmp, $value) = each($params);
                }

                $result[$pname] = $value;
            }
            else
            {
                // param doesn't belong to any option
                $result[] = $p;
            }
        }

        return $result;
    }
}

/**
 * Methods to build command line applications.
 * 
 * namespace spica\core\Console
 *
 * @category   spica
 * @package    core
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 09, 2010
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Console.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaConsoleProgress
{
    /**
     * Renders progress bar only.
     */
    const BAR = 1;
    
    /**
     * Renders time and progress bar.
     */
    const BAR_TIME = 2;

    /**
     * Style in use.
     *
     * @var int
     */
    private $_style;

    /**
     * Constructs an object of <code>SpicaConsoleProgress</code>.
     *
     * @param int $style
     */
    public function __construct($style = 1)
    {
        $this->_style = $style;
    }

    /**
     * Sets style.
     * 
     * @param int $style
     */
    public function changeStyle($style)
    {
        $this->_style = $style;
    }

    /**
     * Renders a progress bar onto console environment (bash only).
     * 
     * @example
     *
     * <code>
     *     for ($x = 0; $x <= 73; $x++) {
     *         SpicaConsole::render($x, 73);
     *     }
     * </code>
     *
     * @param int $done  Current completion ratio in number
     * @param int $total Total completion ratio in number
     * @param int $size  Progress bar size (optional)
     * @param int $prefix  Optional parameter to come back to the beginning of the line
     * @param int $nlOnComplete  Optional parameter to send a new line to console when the progress is complete
     * @return void
     */
    public function render($done, $total, $size = 30, $prefix = "\r", $nlOnComplete = true)
    {
        switch ($this->_style)
        {
            case self::BAR_TIME:
                echo $this->_getTimeBar($done, $total, $size, $prefix, $nlOnComplete);
                break;

            case self::BAR:
            default:
                echo $this->_getBar($done, $total, $size, $prefix, $nlOnComplete);
                break;
        }
    }

    /**
     * Gets time and progress bar.
     *
     * @param int $done  Current completion ratio in number
     * @param int $total Total completion ratio in number
     * @param int $size  Bar size
     * @return void
     */
    private function _getTimeBar($done, $total, $size = 30)
    {

    }

    /**
     * Gets progress bar.
     *
     * @param int $done   Current completion ratio in number
     * @param int $total  Total completion ratio in number
     * @param int $size   Progress bar size (optional)
     * @param int $prefix Optional parameter to come back to the beginning of the line
     * @param int $nlOnComplete Optional parameter to send a new line to console when the progress is complete
     * @return void
     */
    private function _getBar($done, $total, $size = 50, $prefix = "\r", $nlOnComplete = true)
    {
        // if we go over our bound, just ignore it
        if ($done > $total)
        {
            return;
        }

        $percent = (double) ($done / $total);
        $bar = floor($percent * $size);
        $progressBar = sprintf($prefix . "%3d%% ", number_format($percent*100, 0));
        $progressBar .= str_repeat(chr(219), $bar); // "Û"
        $progressBar .= str_repeat(chr(178), $size - $bar); // "²" or superscript 2

        // when done, send a newline
        if ($done == $total && true === $newlineOnComplete)
        {
            $progressBar .= "\n";
        }

        return $progressBar;
    }
}

/**
 * Methods to handle text colors in console interface (Unix or Linux only).
 *
 * namespace spica\core\Console
 *
 * @category   spica
 * @package    core
 * @author     Pham Cong Dinh <pcdinh at phpvietnam dot net>
 * @since      Version 0.3
 * @since      April 09, 2010
 * @copyright  Pham Cong Dinh (http://www.phpvietnam.net)
 * @license    http://www.gnu.org/licenses/lgpl-3.0.txt
 * @version    $Id: Console.php 1869 2011-01-07 18:55:25Z pcdinh $
 */
class SpicaConsoleString
{
    /**
     * Foreground shell colors.
     * ECMA-48 Set Graphics Rendition
     *
     * @var array
     */
    private $_text = array(
        'bold' => '1',
        'italics' => '3',
        'underline' => '4',
        'inverse' => '7',
        'strikethrough' => '9'
    );

    /**
     * Foreground shell colors.
     * ECMA-48 Set Graphics Rendition
     *
     * @var array
     */
    private $_fgColors = array(
        'black' => '0;30',
        'dark_gray' => '1;30',
        'blue' => '0;34',
        'light_blue' => '1;34',
        'green' => '0;32',
        'light_green' => '1;32',
        'cyan' => '0;36',
        'light_cyan' => '1;36',
        'red' => '0;31',
        'light_red' => '1;31',
        'purple' => '0;35',
        'light_purple' => '1;35',
        'brown' => '0;33',
        'yellow' => '1;33',
        'light_gray' => '0;37',
        'white' => '1;37'
    );

    /**
     * Background colors.
     *
     * @var array
     */
    private $_bgColors = array(
        'black' => '40',
        'red' => '41',
        'green' => '42',
        'yellow' => '43',
        'blue' => '44',
        'magenta' => '45',
        'cyan' => '46',
        'light_gray' => '47'
    );

    /**
     * Global options for all string.
     * 
     * @var array
     */
    private $_opt;

    /**
     * Constructs an object of <code>SpicaConsoleProgress</code>.
     *
     * @param array $opt
     */
    public function __construct($opt = array())
    {
        $this->_opt = $opt;
    }

    /**
     * Contructs and gets a string that contains needed attributes to be rendered with colors and custom decolaration onto console.
     *
     * @param  string $string  Original string
     * @param  array  $opt String decoration options
     * @return string
     */
    public function decorate($string, $opt = null)
    {
        $opt = array_merge($this->_opt, $opt);
        $cs = ""; // console string

        if (isset($opt['bold']))
        {
            $cs .= "\x1b[" . $this->_text['bold'] . 'm';
        }

        if (isset($opt['italics']))
        {
            $cs .= "\x1b[" . $this->_text['italics'] . 'm';
        }
        
        if (isset($opt['underline']))
        {
            $cs .= "\x1b[" . $this->_text['underline'] . 'm';
        }

        if (isset($opt['inverse']))
        {
            $cs .= "\x1b[" . $this->_text['inverse'] . 'm';
        }

        if (isset($opt['strikethrough']))
        {
            $cs .= "\x1b[" . $this->_text['strikethrough'] . 'm';
        }

        if (true === isset($opt['fg']) && true === isset($this->_fgColors[$opt['fg']]))
        {
            $cs .= "\033[" . $this->_fgColors[$opt['fg']] . 'm';
        }

        if (true === isset($opt['bg']) && true === isset($this->_bgColors[$opt['bg']]))
        {
            $cs .= "\033[" . $this->_bgColors[$opt['bg']] . 'm';
        }

        return $cs .= $string . "\033[0m";
    }

    /**
     * Gets all foreground color names.
     *
     * @return array
     */
    public function getFgColors()
    {
        return array_keys($this->_fgColors);
    }

    /**
     * Gets all background color names.
     *
     * @return array
     */
    public function getBgColors()
    {
        return array_keys($this->_bgColors);
    }
}

?>